package com.fuled.test.tool_aop;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fuled.test.tool_config.PerformanceUtilGetUserName;
import com.fuled.test.tool_entity.*;
import com.fuled.test.tool_config.IPUtilsPerformanceTool;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author hcy
 * aop类，拦截请求及其经过的controller、service、mapper方法，记录请求信息，并将记录信息存储到redis中
 */
@Component
@Aspect
public class OperationDetailRecordPerformanceTool {

    //记录异常次数
    private static final AtomicInteger exceptionNumber = new AtomicInteger(0);

    //记录不期望返回次数
    private static final AtomicInteger unexpectedReturn = new AtomicInteger(0);

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private PerformanceUtilGetUserName performanceUtilGetUserName;

    //定义切点，匹配所有bean(*Controller)
    @Pointcut("(@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)) && !@within(com.fuled.test.tool_config.DisableUtil)")
    public void pointcutWithinController() {
    }

    //定义切点，匹配所有bean(*Service*)
    @Pointcut("@within(org.springframework.stereotype.Service) && !@within(com.fuled.test.tool_config.DisableUtil)")
    public void pointcutWithinService() {
    }

    //定义切点，匹配所有bean(*Mapper)
    @Pointcut("(@within(org.apache.ibatis.annotations.Mapper) || within(*..mapper..*) || within(*..dao..*)) && !@within(com.fuled.test.tool_config.DisableUtil)")
    public void pointcutWithinMapper() {
    }

    @Around(value = "pointcutWithinController()")
    public Object aroundController(ProceedingJoinPoint proceedingJoinPoint) {
        // 创建操作详情记录实体
        RecordEntityPerformanceTool recordEntityPerformanceTool = new RecordEntityPerformanceTool();
        // 创建操作详情记录控制器
        RecordControllerPerformanceTool recordControllerPerformanceTool = new RecordControllerPerformanceTool();
        // 记录开始时间
        long startTime = 0L;
        // 记录结束时间
        long endTime = 0L;
        Object obj = null;
        try {
            // 执行目标方法
            startTime = System.currentTimeMillis();
            obj = proceedingJoinPoint.proceed();
            endTime = System.currentTimeMillis();
            // 设置返回消息
            recordControllerPerformanceTool.setReturnMessage(obj);
        } catch (Throwable e) {
            e.printStackTrace();
            exceptionNumber.incrementAndGet();
            recordControllerPerformanceTool.setExceptionMessage(e.getMessage());
        }
        // 计算耗时
        long costTime = endTime - startTime;
        // 获取方法参数
        Object[] args = proceedingJoinPoint.getArgs();
        // 获取模块名
        String moduleName = proceedingJoinPoint.getSignature().getDeclaringTypeName();
        // 获取方法名
        String functionName = proceedingJoinPoint.getSignature().getName();
        HttpServletRequest request;
        // 获取用户名
        String username = performanceUtilGetUserName.getUsername();
        try {
            // 获取请求的对象
            request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        } catch (Exception e) {
            System.out.println("无法获取请求");
            //e.printStackTrace();
            return obj;
        }
        try {
            //获取访问ip
            String ipAddress = IPUtilsPerformanceTool.getClientIP(request);

            //通过请求属性获取请求头(时间戳) 作为将来redis的hashKey
            String hashKey = request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).toString();

            String requestId = request.getAttribute(ConstantPerformanceTool.REQUEST_ID).toString();
            // 设置请求id
            recordEntityPerformanceTool.setRequestId(requestId);
            // 设置时间戳
            recordEntityPerformanceTool.setTimestamp(System.currentTimeMillis());
            // 设置用户名
            recordEntityPerformanceTool.setUser(username);
            // 设置IP地址
            recordEntityPerformanceTool.setIpAddress(ipAddress);
            // 设置创建时间
            recordEntityPerformanceTool.setCreateTime(LocalDateTime.now());
            // 设置耗时
            recordEntityPerformanceTool.setTotalCostTime(costTime);
            //设置请求方法
            recordControllerPerformanceTool.setRequestMethod(request.getMethod());
            // 设置函数名
            recordControllerPerformanceTool.setFunctionName(functionName);
            // 设置模块名
            recordControllerPerformanceTool.setModuleName(moduleName);

            // 遍历方法参数，构建参数映射
            Map<String, Object> map = new HashMap<>();
            for (Object arg : args) {
                if(arg != null)
                    map.put(arg.getClass().getName(), arg);
                else map.put("null", "null");
            }
            recordControllerPerformanceTool.setParams(map);

            // 创建ObjectMapper实例
            ObjectMapper objectMapper = new ObjectMapper();
            // 注册JavaTimeModule，支持Java 8日期和时间API
            objectMapper.registerModule(new JavaTimeModule());
            //设置请求id
            recordControllerPerformanceTool.setRequestId(requestId);
            //设置时间戳
            recordControllerPerformanceTool.setTimestamp(System.currentTimeMillis());
            // 设置操作详情记录控制器到操作详情记录实体
            recordEntityPerformanceTool.setRecordControllerPerformanceTool(recordControllerPerformanceTool);
            // 记录redis里面数据的key
            String key = ConstantPerformanceTool.REDIS_KEY_PREFIX + username + "-" + ipAddress;
            // 使用异步方式执行Redis保存操作
            CompletableFuture.runAsync(() -> {
                //1.获取service部分
                String operationDetailRecordEntityExist = (String) stringRedisTemplate.opsForHash().get(key, hashKey);
                //2.将service和dao部分放入entity
                if (operationDetailRecordEntityExist != null) {
                    RecordEntityPerformanceTool recordEntityPerformanceToolInRedis;
                    try {
                        recordEntityPerformanceToolInRedis = objectMapper.readValue(operationDetailRecordEntityExist,
                                RecordEntityPerformanceTool.class);
                        if(recordEntityPerformanceToolInRedis.getRecordServiceImplPerformanceTools() != null)
                            recordEntityPerformanceTool.setRecordServiceImplPerformanceTools(recordEntityPerformanceToolInRedis.getRecordServiceImplPerformanceTools());
                        if(recordEntityPerformanceToolInRedis.getRecordDaoPerformanceTool() != null)
                            recordEntityPerformanceTool.setRecordDaoPerformanceTool(recordEntityPerformanceToolInRedis.getRecordDaoPerformanceTool());
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                }
                //3.序列化操作详情记录entity为JSON字符串
                String jsonString = "";
                try {
                    jsonString = objectMapper.writeValueAsString(recordEntityPerformanceTool);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                //4.发布消息给消费者进行数据记录
                publishMessage(ConstantPerformanceTool.PERFORMANCE_TOOL_ENTItY_CHANNEL, jsonString);
                //Redis保存entity
                stringRedisTemplate.opsForHash().put(key, hashKey, jsonString);
                stringRedisTemplate.expire(key, 10, TimeUnit.MINUTES);
            });
        } catch (Exception e) {
            e.printStackTrace();
            return obj;
        }
        // 继续执行后续操作
        return obj;
    }

    @Around(value = "pointcutWithinService()")
    public Object aroundService(ProceedingJoinPoint proceedingJoinPoint) {
        // 创建RecordServiceImplPerformanceTool对象
        RecordServiceImplPerformanceTool recordServiceImplPerformanceTool = new RecordServiceImplPerformanceTool();
        // 记录开始时间
        long startTime = 0L;
        // 记录结束时间
        long endTime = 0L;
        Object obj = null;
        try {
            // 执行目标方法
            startTime = System.currentTimeMillis();
            obj = proceedingJoinPoint.proceed();
            endTime = System.currentTimeMillis();
            // 设置返回消息
            recordServiceImplPerformanceTool.setReturnMessage(obj);
        } catch (Throwable e) {
            e.printStackTrace();
            recordServiceImplPerformanceTool.setExceptionMessage(e.getMessage());
        }
        HttpServletRequest request;
        Long costTime = endTime - startTime;
        // 获取用户名
        String username = performanceUtilGetUserName.getUsername();
        try {
            // 获取HttpServletRequest对象
            request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        } catch (Exception e) {
            return obj;
        }
        try {
            // 获取客户端IP地址
            String ipAddress = IPUtilsPerformanceTool.getClientIP(request);
            // 检查请求属性是否存在或为空
            if (request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME) == null || request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).equals("")) {
                return obj;
            }
            // 获取请求属性 作为redis里面的hashKey
            String hashKey = request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).toString();

            // 获取方法名
            String functionName = proceedingJoinPoint.getSignature().getName();
            // 获取类名
            String moduleName = proceedingJoinPoint.getSignature().getDeclaringTypeName();

            String requestId = request.getAttribute(ConstantPerformanceTool.REQUEST_ID).toString();

            // 设置请求id
            recordServiceImplPerformanceTool.setRequestId(requestId);
            recordServiceImplPerformanceTool.setModuleName(moduleName);
            recordServiceImplPerformanceTool.setCostTime(costTime);

            Map<String, Object> map = new HashMap<>();
            // 遍历方法参数，并设置到recordServiceImplPerformanceTool中
            for (Object arg : proceedingJoinPoint.getArgs()) {
                if(arg != null)
                    map.put(arg.getClass().getName(), arg);
                else map.put("null", "null");
            }
            recordServiceImplPerformanceTool.setParams(map);

            // 设置方法名
            recordServiceImplPerformanceTool.setFunctionName(functionName);
            // 设置时间戳
            recordServiceImplPerformanceTool.setTimestamp(System.currentTimeMillis());
            // 设置排序号
            recordServiceImplPerformanceTool.setLevel(1);

            // 创建ObjectMapper实例
            ObjectMapper objectMapper = new ObjectMapper();
            // 注册JavaTimeModule，支持Java 8日期和时间API
            objectMapper.registerModule(new JavaTimeModule());

            // 创建RecordEntityPerformanceTool对象
            RecordEntityPerformanceTool recordEntityPerformanceTool = new RecordEntityPerformanceTool();
            // 创建ArrayList用于存储RecordServiceImplPerformanceTool对象
            ArrayList<RecordServiceImplPerformanceTool> serviceList = new ArrayList<>();

            // 拼接Redis键
            String key = ConstantPerformanceTool.REDIS_KEY_PREFIX + username + "-" + ipAddress;
            // 从Redis中获取操作详细记录实体是否存在
            String operationDetailRecordEntityExist = (String) stringRedisTemplate.opsForHash().get(key, hashKey);
            if (operationDetailRecordEntityExist != null) {
                // 反序列化操作详细记录实体
                RecordEntityPerformanceTool operationDetailRecordServiceInRedis = objectMapper.readValue(operationDetailRecordEntityExist,
                        RecordEntityPerformanceTool.class);
                if (operationDetailRecordServiceInRedis.getRecordDaoPerformanceTool() != null) {
                    recordEntityPerformanceTool.setRecordDaoPerformanceTool(operationDetailRecordServiceInRedis.getRecordDaoPerformanceTool());
                }
                // 获取已存在的RecordServiceImplPerformanceTool列表
                ArrayList<RecordServiceImplPerformanceTool> recordServiceImplPerformanceTools = operationDetailRecordServiceInRedis.getRecordServiceImplPerformanceTools();
                // 将已存在的RecordServiceImplPerformanceTool列表添加到serviceList中
                if (recordServiceImplPerformanceTools != null) {
                    // 设置当前RecordServiceImplPerformanceTool的级别为已存在列表的size+1
                    recordServiceImplPerformanceTool.setLevel(recordServiceImplPerformanceTools.size() + 1);
                    serviceList.addAll(recordServiceImplPerformanceTools);
                }
            }
            // 将当前RecordServiceImplPerformanceTool添加到serviceList中
            serviceList.add(recordServiceImplPerformanceTool);
            // 根据级别对serviceList进行排序
            serviceList.sort(Comparator.comparingInt(RecordServiceImplPerformanceTool::getLevel));

            // 设置RecordEntityPerformanceTool的RecordServiceImplPerformanceTool列表
            recordEntityPerformanceTool.setRecordServiceImplPerformanceTools(serviceList);

            // 将RecordEntityPerformanceTool序列化为JSON字符串
            String jsonString = objectMapper.writeValueAsString(recordEntityPerformanceTool);

            // 将JSON字符串保存到Redis中
            // 进行Redis保存
            stringRedisTemplate.opsForHash().put(key, hashKey, jsonString);
            // 设置Redis键的过期时间为10分钟
            stringRedisTemplate.expire(key, 10, TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
            return obj;
        }
        return obj;
    }

    @Around("pointcutWithinMapper()")
    public Object aroundMapper(ProceedingJoinPoint proceedingJoinPoint) {
        // 获取方法名
        String functionName = proceedingJoinPoint.getSignature().getName();
        // 获取类名
        String moduleName = proceedingJoinPoint.getSignature().getDeclaringTypeName();
        if("getObjectType".equals(functionName) || "org.mybatis.spring.mapper.MapperFactoryBean".equals(moduleName)){
            try {
                return proceedingJoinPoint.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
        RecordDaoPerformanceTool recordDaoPerformanceTool = new RecordDaoPerformanceTool();
        // 记录开始时间
        long startTime = 0L;
        // 记录结束时间
        long endTime = 0L;
        Object obj = null;
        try {
            // 执行目标方法
            startTime = System.currentTimeMillis();
            obj = proceedingJoinPoint.proceed();
            endTime = System.currentTimeMillis();
            // 设置返回消息
            recordDaoPerformanceTool.setReturnMessage(obj);
        } catch (Throwable e) {
            e.printStackTrace();
            recordDaoPerformanceTool.setExceptionMessage(e.getMessage());
        }
        Long costTime = endTime - startTime;
        HttpServletRequest request;
        try {
            // 获取HttpServletRequest对象
            request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        } catch (Exception e) {
            return obj;
        }
        if (request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME) == null || request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).equals("")) {
            return obj;
        }
        // 获取用户名
        String username = performanceUtilGetUserName.getUsername();
        try {
            // 获取客户端IP地址
            String ipAddress = IPUtilsPerformanceTool.getClientIP(request);
            // 检查请求属性是否存在或为空
            if (request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME) == null || request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).equals("")) {
                return obj;
            }
            // 获取请求属性 作为redis里面的hashKey
            String hashKey = request.getAttribute(ConstantPerformanceTool.REQUEST_ATTRIBUTE_NAME).toString();

            String requestId = request.getAttribute(ConstantPerformanceTool.REQUEST_ID).toString();

            // 设置请求id
            recordDaoPerformanceTool.setRequestId(requestId);
            recordDaoPerformanceTool.setModuleName(moduleName);
            recordDaoPerformanceTool.setCostTime(costTime);

            Map<String, Object> map = new HashMap<>();
            // 遍历方法参数，并设置到recordServiceImplPerformanceTool中
            for (Object arg : proceedingJoinPoint.getArgs()) {
                if(arg != null)
                    map.put(arg.getClass().getName(), arg);
                else map.put("null", "null");
            }
            recordDaoPerformanceTool.setParams(map);

            // 设置方法名
            recordDaoPerformanceTool.setFunctionName(functionName);
            // 设置时间戳
            recordDaoPerformanceTool.setTimestamp(System.currentTimeMillis());
            // 设置排序号
            recordDaoPerformanceTool.setLevel(1);

            // 创建ObjectMapper实例
            ObjectMapper objectMapper = new ObjectMapper();
            // 注册JavaTimeModule，支持Java 8日期和时间API
            objectMapper.registerModule(new JavaTimeModule());

            // 创建RecordEntityPerformanceTool对象
            RecordEntityPerformanceTool recordEntityPerformanceTool = new RecordEntityPerformanceTool();
            // 创建ArrayList用于存储RecordDaoPerformanceTool对象
            ArrayList<RecordDaoPerformanceTool> daoList = new ArrayList<>();

            // 拼接Redis键
            String key = ConstantPerformanceTool.REDIS_KEY_PREFIX + username + "-" + ipAddress;
            // 从Redis中获取操作详细记录实体是否存在
            String operationDetailRecordEntityExist = (String) stringRedisTemplate.opsForHash().get(key, hashKey);
            if (operationDetailRecordEntityExist != null) {
                // 反序列化操作详细记录实体
                RecordEntityPerformanceTool operationDetailRecordServiceInRedis = objectMapper.readValue(operationDetailRecordEntityExist,
                        RecordEntityPerformanceTool.class);
                //获取已存在的RecordDaoPerformanceTool列表
                ArrayList<RecordDaoPerformanceTool> recordDaoPerformanceToolList = operationDetailRecordServiceInRedis.getRecordDaoPerformanceTool();
                //将已存在的RecordDaoPerformanceTool列表添加到daoList中
                if (recordDaoPerformanceToolList != null){
                    daoList.addAll(recordDaoPerformanceToolList);
                    //设置当前RecordDaoPerformanceTool的级别
                    recordDaoPerformanceTool.setLevel(recordDaoPerformanceToolList.size() + 1);
                }
            }

            // 将当前RecordDaoPerformanceTool添加到recordDaoPerformanceToolList中
            daoList.add(recordDaoPerformanceTool);
            //根据级别对daoList进行排序
            daoList.sort(Comparator.comparingInt(RecordDaoPerformanceTool::getLevel));
            //设置RecordEntityPerformanceTool的RecordDaoPerformanceTool列表
            recordEntityPerformanceTool.setRecordDaoPerformanceTool(daoList);

            // 将RecordEntityPerformanceTool序列化为JSON字符串
            String jsonString = objectMapper.writeValueAsString(recordEntityPerformanceTool);

            // 将JSON字符串保存到Redis中
            stringRedisTemplate.opsForHash().put(key, hashKey, jsonString);
            // 设置Redis键的过期时间为10分钟
            stringRedisTemplate.expire(key, 10, TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
            return obj;
        }
        return obj;
    }

    //发布
    public void publishMessage(String channel, String message) {
        stringRedisTemplate.execute((RedisCallback<Long>) connection -> {
            byte[] rawChannel = channel.getBytes(StandardCharsets.UTF_8);
            byte[] rawMessage = message.getBytes(StandardCharsets.UTF_8);
            return connection.publish(rawChannel, rawMessage);
        });
    }

}
